﻿using System.Windows.Forms;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;
using CommonTypes.Client;
using CommonTypes.Server;
using System;
using PuppetMaster.Menus;

namespace PuppetMaster
{
    public class PuppetMaster
    {
        //Mapped by "ip:port"
        private Dictionary<String, IPMToClient> _clientsProxiesByLocation;
        private Dictionary<String, IPMToServer> _serversProxiesByLocation;

        //Mapped by name
        private Dictionary<String, IPMToClient> _clientsProxiesByName;
        private Dictionary<String, IPMToServer> _serversProxiesByName;

        private ArrayList _clientProcesses;
        private ArrayList _serverProcesses;


        public PuppetMaster()
        {
            _clientsProxiesByLocation = new Dictionary<string, IPMToClient>();
            _serversProxiesByLocation = new Dictionary<string, IPMToServer>();
            _clientsProxiesByName = new Dictionary<string, IPMToClient>();
            _serversProxiesByName = new Dictionary<string, IPMToServer>();
            _clientProcesses = new ArrayList();
            _serverProcesses = new ArrayList();
        }

        private void AddClientToMaps(string name, string location, IPMToClient proxy)
        {
            if(!_clientsProxiesByLocation.ContainsKey(location))
                _clientsProxiesByLocation.Add(location, proxy);
            else
                _clientsProxiesByLocation[location] = proxy;

            if(!_clientsProxiesByName.ContainsKey(name))
                _clientsProxiesByName.Add(name, proxy);
            else
                _clientsProxiesByName[name] = proxy;
        }

        private void AddServerToMaps(string name, string location, IPMToServer proxy)
        {
            if (!_serversProxiesByLocation.ContainsKey(location))
                _serversProxiesByLocation.Add(location, proxy);
            else
                _serversProxiesByLocation[location] = proxy;

            if (!_serversProxiesByName.ContainsKey(name))
                _serversProxiesByName.Add(name, proxy);
            else
                _serversProxiesByName[name] = proxy;
        }

        private void ClearServerMaps()
        {
            _serversProxiesByLocation.Clear();
            _serversProxiesByName.Clear();
        }

        private void ClearClientMaps()
        {
            _clientsProxiesByLocation.Clear();
            _clientsProxiesByName.Clear();
        }

        public void ConnectClient(String name, String location, bool gui)
        {
            //If already exists, don't start a new process
            if (_clientsProxiesByLocation.ContainsKey(location) && _clientsProxiesByName.ContainsKey(name)
                && _clientsProxiesByName[name] == _clientsProxiesByLocation[location])
            {
                _clientsProxiesByName[name].Connect();
                return;
            }
            
            string[] split = location.Split(':');
            int port = Int32.Parse(split[1]);
            
            Process client = new Process();
            _clientProcesses.Add(client);

            //Melhorar...
            client.StartInfo.FileName = "..\\..\\..\\Client\\bin\\Debug\\Client.exe";
            client.StartInfo.WorkingDirectory = "..\\..\\..\\Client\\bin\\Debug";

            client.StartInfo.Arguments = name + " " + port;
            if (!gui)
                client.StartInfo.Arguments += " noGUI";
            client.Start();

            //Get the proxy of the new client, PuppetMaster only works locally
            IPMToClient clientProxy = (IPMToClient)Activator.GetObject(typeof(IPMToClient), "tcp://" + location + "/PMToClient");
            AddClientToMaps(name, location, clientProxy);

            _clientsProxiesByName[name].Connect();
        }


        public void ConnectServer(int id, string location, bool gui)
        {
            //If already exists, don't start a new process
            if (_serversProxiesByLocation.ContainsKey(location) && _serversProxiesByName.ContainsKey("server"+id)
                && _serversProxiesByName["server" + id] == _serversProxiesByLocation[location])
            {
                _serversProxiesByLocation[location].Connect();
                return;
            }
            
            string[] split = location.Split(':');
            int port = Int32.Parse(split[1]);

            Process server = new Process();
            _serverProcesses.Add(server);

            server.StartInfo.FileName = "..\\..\\..\\Server\\bin\\Debug\\Server.exe";
            server.StartInfo.WorkingDirectory = "..\\..\\..\\Server\\bin\\Debug";

            server.StartInfo.Arguments = "server" + id + " false";
            if (!gui)
                server.StartInfo.Arguments += " noGUI";
            server.Start();

            //Get server proxy
            IPMToServer serverProxy = (IPMToServer)Activator.GetObject(typeof(IPMToServer), "tcp://" + location + "/PMToServer");
            AddServerToMaps("server" + id, location, serverProxy);

            _serversProxiesByName["server" + id].Connect();
        }

        public void DisconectClient(string location)
        {
            if(_clientsProxiesByLocation.ContainsKey(location))
                _clientsProxiesByLocation[location].Disconnect();
        }

        public void DisconectServer(string location)
        {
            if (_serversProxiesByLocation.ContainsKey(location))
                _serversProxiesByLocation[location].Disconnect();
        }

        public void ReadCalendar(string location, string name)
        {
            ArrayList calendar = null;
            
            if(_clientsProxiesByLocation.ContainsKey(location))
                calendar = _clientsProxiesByLocation[location].ReadCalendar();

            new CalendarViewer(name, calendar).Show();
        }


        public void BeginReservation(String clientToInitiate, ArrayList slotNumbers, String clientsToInvite, String reservationName)
        {
            _clientsProxiesByName[clientToInitiate].BeginReservation(slotNumbers, clientsToInvite, reservationName);
        }

        public void KillAllClients()
        {
            foreach (Process p in _clientProcesses)
            {
                try
                {
                    p.Kill();
                }
                catch { }
            }
            ClearClientMaps();
            _clientProcesses.Clear();
        }

        public void KillAllServers()
        {
            foreach (Process p in _serverProcesses)
            {
                try
                {
                    p.Kill();
                }
                catch { }
            }
            ClearServerMaps();
            _serverProcesses.Clear();
        }

        public void Run()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new PMMainMenu(this));
        }
    }
}
